#include "Logic.h"

// ************************************************************************ //
// This is the public function for UI to call
// Scroll down to see how the sub-functions are implemented
// All logic is done in SLAP
// ************************************************************************ //
string Logic::executeUserCommand(string userCommand, vector<Task>& tasksVector) {
	Parser::PARSED_COMMAND parsedCommand;
	Parser::CommandType commandType;

	string statusMessage;

	parsedCommand = _parser->parseUserCommand(userCommand);
	commandType = parsedCommand.commandType;

	if( commandType == Parser::ADD_FLOATING_TASK ) {
		try {
			statusMessage = addFloatingTask(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::ADD_DEADLINE_TASK ) {
		try {
			statusMessage = addDeadlineTask(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::ADD_TIMED_TASK ) {
		try {
			statusMessage = addTimedTask(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::DELETE_TASKID ) {
		try {
			statusMessage = deleteTaskByIndex(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::DELETE_DONE ) {
		try {
			statusMessage = deleteDoneTask(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::DELETE_SEARCHTERM ) {
			try {
				statusMessage = deleteTaskByString(parsedCommand, tasksVector);
			} catch (string err) {
				statusMessage = err;
			}
	} else if( commandType == Parser::LIST_ALL ) {
		try {
			statusMessage = listAllTasks(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::LIST_MONTH ) {
		try {
			statusMessage = listTaskByMonth(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::LIST_DAY ) {
		try {
			statusMessage = listTaskByDay(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::LIST_WEEK ) {
		try {
			statusMessage = listTaskNextWeek(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::LIST_SEARCH_TERM ) {
		try {
			statusMessage = listTaskBySearchTerm(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::EDIT_RENAME ) {
		try {
			statusMessage = editTaskByName(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::EDIT_START_TIME ) {
		try {
			statusMessage = editTaskByStartTime(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::EDIT_END_TIME ) {
		try {
			statusMessage = editTaskByEndTime(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::EDIT_START_DATETIME ) {
		try {
			statusMessage = editTaskByStartDateTime(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::EDIT_END_DATETIME ) {
		try {
			statusMessage = editTaskByEndDateTime(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::SORT_ALPHABET ) {
		try {
			statusMessage = sortTasksAlphabetically(parsedCommand, tasksVector);
		}  catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::SORT_COMPLETED ) {
		try {
			statusMessage = sortTasksByDone(parsedCommand, tasksVector);
		}  catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::SORT_FLAG ) {
		try {
			statusMessage = sortTasksByFlag(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::SORT_TIME ) {
		try {
			statusMessage = sortTasksByDateTime(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::DONE_TASKID ) {
		try {
			statusMessage = doneByIndex(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::DONE_SEARCHTERM ) {
		try {
			statusMessage = doneBySearchTerm(parsedCommand, tasksVector);
		} catch (string err) {
			statusMessage = err;
		}
//	} else if( commandType == Parser::CLEAR_TASK ) {
//		try {
//			statusMessage = clearTask(tasksVector);
//		} catch (string err) {
//			statusMessage = err;
//		}
//	} else if( commandType == Parser::EXIT ) {
//		exit(1); // let UI handle the quit by returning a statusMessage
	}

	// if( commandType != Parser::UNDO )
	updateUndoState();

	return statusMessage;
}
// ************************************************************************ //
// ***							END OF API								*** //
// ************************************************************************ //




//*********************************************************//
//***				POINTS TO NOTE ON LOGIC				***//
//*********************************************************//
// 1. tasksVector is updated for UI display
// 2. 10 undo writer objects + 1 main writer
// 3. undo writer objects records 1 less function call
// 4. _undoState indexes current undo writer object
// 5. logErrors called upon internal error
//*********************************************************//
//***					END OF SECTION					***//
//*********************************************************//




// ******************************************//
// FUNCTIONS BELOW ARE PRIVATE SUB-FUNCTIONS //
// ******************************************//



// ***************************//
// START OF ADD SUB-FUNCTIONS //
// ***************************//
string Logic::addFloatingTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool clash;
	int currentTaskID;
	int repeats;
	stringstream repeatCount;

	// empty UI's display
	emptyTasksVector(tasksVector);

	// set _task attributes
	string taskName = parsedCommand.addTaskName;
	ptime startTime = parsedCommand.addStartTime;
	ptime endTime = parsedCommand.addEndTime;

	// check if task name exist
	// if exist, append: -number
	repeats = isExistName(taskName);
	if( repeats > 0 ) {
		taskName = rectifyRepeatName(repeats, taskName, 0, taskName);
	}

	// task creation
	_task = Task(taskName, 0, Task::FLOATING, false, false, startTime, endTime);

	clash = isDateClash(parsedCommand, tasksVector);

	// add into internal data
	dataFeedback = _data->executeAdd(_task);

	// update UI's display
	currentTaskID = _data->getCurrentTaskID();
	_task.setTaskID(currentTaskID);
	tasksVector.push_back(_task);
	
	// ok for clashes
	// inform user of clash via tasksVector (display)
	// return status message to UI
	if( dataFeedback == Data::SUCCESS ) {
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
		if( !clash ) {
			statusMessage = ADD_SUCCESS_MESSAGE;
		} else {
			statusMessage = ADD_ERROR_CLASH_MESSAGE;
		}
	} else {
		statusMessage = ADD_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}
	
	return statusMessage;
}

string Logic::addDeadlineTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool clash;
	int currentTaskID;

	// empty UI's display
	emptyTasksVector(tasksVector);

	// set _task attributes
	string taskName = parsedCommand.addTaskName;
	ptime startTime = parsedCommand.addStartTime;
	ptime endTime = parsedCommand.addEndTime;

	// task creation
	_task = Task(taskName, 0, Task::DEADLINE, false, false, startTime, endTime);

	clash = isDateClash(parsedCommand, tasksVector);

	// add to internal data
	dataFeedback = _data->executeAdd(_task);

	// update UI's display
	currentTaskID = _data->getCurrentTaskID();
	_task.setTaskID(currentTaskID);
	tasksVector.push_back(_task);

	// return status message to UI
	if( dataFeedback == Data::SUCCESS ) {
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
		if( !clash ) {
			statusMessage = ADD_SUCCESS_MESSAGE;
		} else {
			statusMessage = ADD_ERROR_CLASH_MESSAGE;
		}
	} else {
		statusMessage = ADD_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::addTimedTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool clash;
	int currentTaskID;

	// empty UI's display
	emptyTasksVector(tasksVector);

	// set _task attributes
	string taskName = parsedCommand.addTaskName;
	ptime startTime = parsedCommand.addStartTime;
	ptime endTime = parsedCommand.addEndTime;

	// task creation
	_task = Task(taskName, 0, Task::TIMED, false, false, startTime, endTime);

	clash = isDateClash(parsedCommand, tasksVector);

	// add to internal data
	dataFeedback = _data->executeAdd(_task);

	// update UI's display
	currentTaskID = _data->getCurrentTaskID();
	_task.setTaskID(currentTaskID);
	tasksVector.push_back(_task);

	// return status message to UI
	if( dataFeedback == Data::SUCCESS ) {
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
		if( !clash ) {
			statusMessage = ADD_SUCCESS_MESSAGE;
		} else {
			statusMessage = ADD_ERROR_CLASH_MESSAGE;
		}
	} else {
		statusMessage = ADD_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}


int Logic::isExistName(string taskName) {

	int repeats;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	dataFeedback = _data->getByName(tasks, taskName);

	repeats = tasks.size();

	return repeats;
}

// this recursive function appends -1 to similar tasks name
// not the best method but enough to differentiate
string Logic::rectifyRepeatName(int repeats, string newTaskName, int append, string taskName) {

	stringstream count;
	
	if( repeats > 0 ) {
		append++;
		count << append;
		newTaskName = taskName + "(" + count.str() + ")";
		repeats = isExistName(newTaskName);
		newTaskName = rectifyRepeatName(repeats, newTaskName, append, taskName);
	}

	return newTaskName;
}

// returns a vector containing the tasks that clashed
// either by start|end time
bool Logic::isDateClash(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	bool isClash;
	string taskName;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	taskName = parsedCommand.addTaskName;

	dataFeedback = _data->getAll(tasks);

	for( unsigned int i=0; i<tasks.size(); i++ ) {
		if( tasks[i].getTaskType() != Task::FLOATING ) {
			if( tasks[i].getEndDateTime() > parsedCommand.addStartTime
				&& tasks[i].getStartDateTime() <= parsedCommand.addEndTime ) {
					tasksVector.push_back(tasks[i]);
			}
		}
	}

	if( tasksVector.empty() ) {
		isClash = false;
	} else {
		isClash = true;
	}

	return isClash;
}
// *************************//
// END OF ADD SUB-FUNCTIONS //
// *************************//


// ******************************//
// START OF DELETE SUB-FUNCTIONS //
// ******************************//
string Logic::deleteTaskByString(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	string term;
	bool typeName;
	bool typeSearchTerm;

	term = parsedCommand.deleteSearchTerm;

	typeName = isDeleteByName(term);
	typeSearchTerm = isDeleteBySearchTerm(term);

	if( typeName ) {
		statusMessage = deleteTaskByName(parsedCommand, tasksVector);
	} else if( typeSearchTerm ) {
		statusMessage = deleteTaskBySearchTerm(parsedCommand, tasksVector);
	}

	return statusMessage;
}

// this function determines whether it deletes by done
// returns TRUE if term matches keyword done
// returns FALSE otherwise
bool Logic::isDeleteByDone(string term) {
	
	bool isDeleteTypeDone;
	if( term == DELETE_TYPE_DONE ) {
		isDeleteTypeDone = true;
	} else {
		isDeleteTypeDone = false;
	}

	return isDeleteTypeDone;
}

bool Logic::isDeleteByName(string term) {

	bool isDeleteTypeName;

	vector<Task> tasks;
	_data->getByName(tasks, term);

	if( tasks.empty() ) {
		isDeleteTypeName = false;
	} else {
		isDeleteTypeName = true;
	}

	return isDeleteTypeName;
}

bool Logic::isDeleteBySearchTerm(string term) {

	bool isDeleteSearchTerm;

	vector<Task> tasks;
	_data->getBySearchTerm(tasks, term);

	if( tasks.empty() ) {
		isDeleteSearchTerm = false;
	} else {
		isDeleteSearchTerm = true;
	}

	return isDeleteSearchTerm;
}

bool Logic::isMultipleMatch(int size) {
	
	bool multipleMatch;
	
	if( size == 1 ) {
		multipleMatch = false;
	} else {
		multipleMatch = true;
	}

	return multipleMatch;
}

string Logic::deleteTaskByIndex(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool outOfBounds;
	
	int index = parsedCommand.deleteTaskID;

	outOfBounds = isOutOfBounds(index, tasksVector.size());
	
	if( outOfBounds ) {
		throw(INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE);
	}

	int taskId = tasksVector[index-1].getTaskID();

	dataFeedback = _data->executeDeleteTaskID(taskId);

	if( dataFeedback == Data::SUCCESS ) {
		tasksVector.erase(tasksVector.begin()+index-1);
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
		statusMessage = DELETE_SUCCESS_MESSAGE;
	} else {
		statusMessage = DELETE_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::deleteTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	string taskName;
	int taskId;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	vector<Task> displayTasks;
	bool multipleMatch;
	bool executed;

	taskName = parsedCommand.deleteSearchTerm;

	// insert try catch block here
	dataFeedback = _data->getByName(tasks ,taskName);
	
	emptyTasksVector(tasksVector);

	if( tasks.empty() ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	multipleMatch = isMultipleMatch(tasks.size());

	if( multipleMatch ) {
		rePopulateUIVector(tasks, tasksVector);
		executed = false;
	} else {
		taskId = tasks[FIRST_POSITION].getTaskID();
		tasksVector.push_back(tasks[FIRST_POSITION]);
		dataFeedback = _data->executeDeleteTaskID(taskId);
		executed = true;
	}

	if( executed == true && dataFeedback == Data::SUCCESS ) {
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
		statusMessage = DELETE_SUCCESS_MESSAGE;
	} else {
		statusMessage = DELETE_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::deleteTaskBySearchTerm(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	string searchTerm;
	int taskId;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool multipleMatch;
	bool executed;

	searchTerm = parsedCommand.deleteSearchTerm;

	dataFeedback = _data->getBySearchTerm(tasks, searchTerm);

	emptyTasksVector(tasksVector);

	if( tasks.empty() ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	multipleMatch = isMultipleMatch(tasks.size());

	if( multipleMatch ) {
		rePopulateUIVector(tasks, tasksVector);
		executed = false;
	} else {
		taskId = tasks[FIRST_POSITION].getTaskID();
		tasksVector.push_back(tasks[FIRST_POSITION]);
		dataFeedback = _data->executeDeleteTaskID(taskId);
		executed = true;
	}

	if( executed == true && dataFeedback == Data::SUCCESS ) {
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
		statusMessage = DELETE_SUCCESS_MESSAGE;
	} else {
		statusMessage = DELETE_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::deleteDoneTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	int taskId;
	Data::DataFeedback dataFeedback;

	vector<Task> tasks;

	dataFeedback = _data->getByDone(tasks);
	
	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	rePopulateUIVector(tasks, tasksVector);

	for( unsigned int i=0; i<tasks.size(); i++ ) {
		taskId = tasks[i].getTaskID();
		dataFeedback = _data->executeDeleteTaskID(taskId);
	}

	if(dataFeedback == Data::SUCCESS ) {
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
		statusMessage = DELETE_SUCCESS_MESSAGE;
	} else {
		statusMessage = DELETE_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}
// ****************************//
// END OF DELETE SUB-FUNCTIONS //
// ****************************//


// ****************************//
// START OF LIST SUB-FUNCTIONS //
// ****************************//
string Logic::listAllTasks(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	dataFeedback = _data->getAll(tasks);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	rePopulateUIVector(tasks, tasksVector);

	if( dataFeedback == Data::SUCCESS ) {
		statusMessage = LIST_SUCCESS_MESSAGE;
	} else {
		statusMessage = LIST_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::listTaskBySearchTerm(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	string searchTerm;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	searchTerm = parsedCommand.listSearchTerm;
	
	dataFeedback = _data->getBySearchTerm(tasks, searchTerm);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	rePopulateUIVector(tasks, tasksVector);

	if( dataFeedback == Data::SUCCESS ) {
		statusMessage = LIST_SUCCESS_MESSAGE;
	} else {
		statusMessage = LIST_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::listTaskByDay(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	ptime time;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	time = parsedCommand.addStartTime;

	dataFeedback = _data->getByTime(tasks, time, time);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	rePopulateUIVector(tasks, tasksVector);

	if( dataFeedback == Data::SUCCESS ) {
		statusMessage = LIST_SUCCESS_MESSAGE;
	} else {
		statusMessage = LIST_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::listTaskByDate(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	ptime time;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	time = parsedCommand.addStartTime;

	dataFeedback = _data->getByTime(tasks, time, time);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	rePopulateUIVector(tasks, tasksVector);

	if( dataFeedback == Data::SUCCESS ) {
		statusMessage = LIST_SUCCESS_MESSAGE;
	} else {
		statusMessage = LIST_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::listTaskNextWeek(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	ptime week;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	// ptime t = parsedCommand.startTime + dd(7);
	// temporary ptime week;
	
	dataFeedback = _data->getByTime(tasks, week, week);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	rePopulateUIVector(tasks, tasksVector);

	if( dataFeedback == Data::SUCCESS ) {
		statusMessage = LIST_SUCCESS_MESSAGE;
	} else {
		statusMessage = LIST_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::listTaskByMonth(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	ptime month;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	month = parsedCommand.addStartTime;

	dataFeedback = _data->getByTime(tasks, month, month);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	rePopulateUIVector(tasks, tasksVector);

	if( dataFeedback == Data::SUCCESS ) {
		statusMessage = LIST_SUCCESS_MESSAGE;
	} else {
		statusMessage = LIST_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::listTaskByDone(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	dataFeedback = _data->getByDone(tasks);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	rePopulateUIVector(tasks, tasksVector);

	if( dataFeedback == Data::SUCCESS ) {
		statusMessage = LIST_SUCCESS_MESSAGE;
	} else {
		statusMessage = LIST_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}
// **************************//
// END OF LIST SUB-FUNCTIONS //
// **************************//

// ************************//
// START OF CLEAR FUNCTION //
// ************************//
string Logic::clearTask(vector<Task>& tasksVector) {
	
	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	dataFeedback = _data->executeClear();

	emptyTasksVector(tasksVector);

	if( dataFeedback == Data::SUCCESS ) {
		_data->getAll(tasks);
		_writer->write(tasks);
		statusMessage = CLEAR_SUCCESS_MESSAGE;
	} else {
		statusMessage = CLEAR_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}
// **********************//
// END OF CLEAR FUNCTION //
// **********************//


// ****************************//
// START OF FLAG SUB-FUNCTIONS //
// ****************************//
string Logic::flagTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	string taskName;
	int taskID;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool multipleMatch;
	bool executed;
	bool flagState;

	taskName = parsedCommand.editTaskName;

	_data->getByName(tasks, taskName);

	if( tasks.empty() ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);

	multipleMatch = isMultipleMatch(tasks.size());

	if( multipleMatch ) {
		rePopulateUIVector(tasks, tasksVector);
		executed = false;
	} else {
		_task = tasks[FIRST_POSITION];
		flagState = _task.getFlag();
		_task.setFlag(!flagState);
		tasksVector.push_back(_task);
		taskID = _task.getTaskID();
		dataFeedback = _data->executeEdit(_task);
		executed = true;
	}

	if( executed == true && dataFeedback == Data::SUCCESS ) {
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
		statusMessage = FLAG_SUCCESS_MESSAGE;
	} else {
		statusMessage = FLAG_ERROR_MESSAGE;
		logErrors(statusMessage, _undoState);
	}

	return statusMessage;
}

string Logic::flagTaskByIndex(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	string taskName;
	int taskID;
	int index;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool flagState;
	bool outOfBounds;

	index = parsedCommand.editTaskID;

	outOfBounds = isOutOfBounds(index, tasksVector.size());

	if( outOfBounds ) {
		throw(INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE);
	}

	taskID = tasksVector[index-1].getTaskID();

	_data->getByID(tasks, taskID);

	if( tasks.empty() ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	if( tasksVector[index-1].getFlag() == false ) {
		flagState = true;
	} else {
		flagState = false;
	}

	_task = tasksVector[index-1];
	_task.setFlag(flagState);

	dataFeedback = _data->executeEdit(_task);

	if( dataFeedback == Data::FAILURE ) {
		throw(FLAG_ERROR_MESSAGE);
		logErrors(FLAG_ERROR_MESSAGE, _undoState);
	}

	if( dataFeedback == Data::SUCCESS ) {
		emptyTasksVector(tasksVector);
		tasksVector.push_back(_task);
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);

		if( flagState ) {
			statusMessage = FLAG_SUCCESS_MESSAGE;
		} else {
			statusMessage = UNFLAG_SUCCESS_MESSAGE;
		}
	}

	return statusMessage;
}
// **************************//
// END OF FLAG SUB-FUNCTIONS //
// **************************//


// ****************************//
// START OF SORT SUB-FUNCTIONS //
// ****************************//
string Logic::sortTasksAlphabetically(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	// sort UI's display
	sort(tasksVector.begin(), tasksVector.end(), sortObjAlpha);

	dataFeedback = _data->getAll(tasks);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	if( dataFeedback == Data::FAILURE ) {
		// consider changing to internal error code
		throw(SORT_ERROR_MESSAGE);
	}

	sort(tasks.begin(), tasks.end(), sortObjAlpha);
	_writer->write(tasks);
	_data->executeLoad(tasks);

	if( tasksVector.empty() ) {
		statusMessage = SORT_INTERNAL_SUCCESS_MESSAGE;
	} else {
		statusMessage = SORT_ALPHA_SUCCESS_MESSAGE;
	}

	return statusMessage;
}

string Logic::sortTasksByFlag(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	// sort UI's display
	sortByFlag(tasksVector);
	
	dataFeedback = _data->getAll(tasks);
	
	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	if( dataFeedback == Data::FAILURE ) {
		// consider changing to internal error code
		logErrors(SORT_FLAG_ERROR_MESSAGE, _undoState);
		throw(SORT_FLAG_ERROR_MESSAGE);
	}
	
	sortByFlag(tasks);
	_data->executeLoad(tasks);
	_writer->write(tasks);

	if( tasksVector.empty() ) {
		statusMessage = SORT_INTERNAL_SUCCESS_MESSAGE;
	} else {
		statusMessage = SORT_FLAG_SUCCESS_MESSAGE;
	}

	return statusMessage;
}

string Logic::sortTasksByDateTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	// sort UI's display
	sort(tasksVector.begin(), tasksVector.end(), sortObjDateTime);

	// get internal data
	dataFeedback = _data->getAll(tasks);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}

	if( dataFeedback == Data::FAILURE ) {
		// consider changing to internal error code
		logErrors(SORT_DATE_TIME_ERROR_MESSAGE, _undoState);
		throw(SORT_DATE_TIME_ERROR_MESSAGE);
	}

	sort(tasks.begin(), tasks.end(), sortObjDateTime);
	_data->executeLoad(tasks);
	_writer->write(tasks);

	if( tasksVector.empty() ) {
		statusMessage = SORT_INTERNAL_SUCCESS_MESSAGE;
	} else {
		statusMessage = SORT_DATE_TIME_SUCCESS_MESSAGE;
	}

	return statusMessage;
}

string Logic::sortTasksByDone(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;

	// sort UI's display
	sortByDone(tasksVector);

	// get internal data
	dataFeedback = _data->getAll(tasks);

	if( dataFeedback == Data::FAILURE ) {
		logErrors(SORT_DONE_ERROR_MESSAGE, _undoState);
	}

	sortByDone(tasks);
	_data->executeClear();
	_data->executeLoad(tasks);
	_writer->write(tasks);
	
	if( tasksVector.empty() ) {
		statusMessage = SORT_INTERNAL_SUCCESS_MESSAGE;
	} else {
		statusMessage = SORT_SUCCESS_MESSAGE;
	}

	return statusMessage;
}

void Logic::sortByDone(vector<Task>& tasksVector) {
	
	vector<Task> doneTasks;
	vector<Task> undoneTasks;

	// populate doneTasks with done tasks
	for( unsigned int i=0; i<tasksVector.size(); i++ ) {
		if(tasksVector[i].getDone() == true) {
			doneTasks.push_back(tasksVector[i]);
		}
	}
	// sort the done tasks alphabetically
	sort(doneTasks.begin(), doneTasks.end(), sortObjAlpha);

	// populate undoneTasks with undone tasks
	for( unsigned int j=0; j<tasksVector.size(); j++ ) {
		if( tasksVector[j].getDone() == false ) {
			undoneTasks.push_back(tasksVector[j]);
		}
	}
	// sort the undone tasks alphabetically
	sort(undoneTasks.begin(), undoneTasks.end(), sortObjAlpha);

	emptyTasksVector(tasksVector);

	for( unsigned int k=0; k<doneTasks.size(); k++ ) {
		tasksVector.push_back(doneTasks[k]);
	}
	
	for( unsigned int l=0; l<undoneTasks.size(); l++ ) {
		tasksVector.push_back(undoneTasks[l]);
	}
}

void Logic::sortByFlag(vector<Task>& tasksVector) {

	vector<Task> flaggedTasks;
	vector<Task> unFlaggedTasks;

	// populate flaggedTasks with flagged tasks
	for( unsigned int i=0; i<tasksVector.size(); i++ ) {
		if(tasksVector[i].getDone() == true) {
			flaggedTasks.push_back(tasksVector[i]);
		}
	}
	// sort the flaggedTasks alphabetically
	sort(flaggedTasks.begin(), flaggedTasks.end(), sortObjAlpha);

	// populate unFlaggedTasks with unFlaggedTasks tasks
	for( unsigned int j=0; j<tasksVector.size(); j++ ) {
		if( tasksVector[j].getDone() == false ) {
			unFlaggedTasks.push_back(tasksVector[j]);
		}
	}
	// sort the unFlaggedTasks alphabetically
	sort(unFlaggedTasks.begin(), unFlaggedTasks.end(), sortObjAlpha);

	emptyTasksVector(tasksVector);

	for( unsigned int k=0; k<flaggedTasks.size(); k++ ) {
		tasksVector.push_back(flaggedTasks[k]);
	}
	
	for( unsigned int l=0; l<unFlaggedTasks.size(); l++ ) {
		tasksVector.push_back(unFlaggedTasks[l]);
	}
}
// **************************//
// END OF SORT SUB-FUNCTIONS //
// **************************//


// ****************************//
// START OF EDIT SUB-FUNCTIONS //
// ****************************//
string Logic::editTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	int index;
	int taskID;
	string newTaskName;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool outOfBounds;

	// get user input
	index = parsedCommand.editTaskID;
	newTaskName = parsedCommand.editTaskName;

	outOfBounds = isOutOfBounds(index, tasksVector.size());

	// empty and out of bounds taken care of here
	if( outOfBounds ) {
		throw(INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE);
	}
	
	// find task on display
	taskID = tasksVector[index-1].getTaskID();

	// if no changes made
	if( tasksVector[index-1].getTaskName() == newTaskName ) {
		throw(NO_CHANGES_ERROR_MESSAGE);
	}

	// set editted task attributes
	tasksVector[index-1].setTaskName(newTaskName);
	_task = tasksVector[index-1];

	// update internal data
	dataFeedback = _data->executeEdit(_task);

	if( dataFeedback == Data::FAILURE ) {
		logErrors(EDIT_ERROR_MESSAGE, _undoState);
		throw(EDIT_ERROR_MESSAGE);
	}
	
	emptyTasksVector(tasksVector);
	_data->getByID(tasks, taskID);
	rePopulateUIVector(tasks, tasksVector);
	tasks.clear();
	_data->getAll(tasks);
	_writer->write(tasks);

	statusMessage = EDIT_SUCCESS_MESSAGE;

	return statusMessage;
}

string Logic::editTaskByStartTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	int index;
	int taskID;
	time_duration newStartTime;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool outOfBounds;

	// get user input
	index = parsedCommand.editTaskID;
	newStartTime = parsedCommand.editStartTime;

	outOfBounds = isOutOfBounds(index, tasksVector.size());

	if( outOfBounds ) {
		throw(INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE);
	}

	// find task on display
	taskID = tasksVector[index-1].getTaskID();

	if( tasksVector[index-1].getStartTime() == newStartTime ) {
		throw(NO_CHANGES_ERROR_MESSAGE);
	}

	// set editted task attributes
	tasksVector[index-1].setStartTime(newStartTime);
	_task = tasksVector[index-1];

	// update internal data
	dataFeedback = _data->executeEdit(_task);

	if( dataFeedback == Data::FAILURE ) {
		logErrors(EDIT_ERROR_MESSAGE, _undoState);
		throw(EDIT_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	_data->getByID(tasks, taskID);
	rePopulateUIVector(tasks, tasksVector);
	tasks.clear();
	_data->getAll(tasks);
	_writer->write(tasks);

	statusMessage = EDIT_SUCCESS_MESSAGE;

	return statusMessage;
}

string Logic::editTaskByEndTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	int index;
	int taskID;
	time_duration newEndTime;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool outOfBounds;

	// get user input
	index = parsedCommand.editTaskID;
	newEndTime = parsedCommand.editEndTime;

	outOfBounds = isOutOfBounds(index, tasksVector.size());

	if( outOfBounds ) {
		throw(INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE);
	}

	// find task on display
	taskID = tasksVector[index-1].getTaskID();

	if( tasksVector[index-1].getEndTime() == newEndTime ) {
		throw(NO_CHANGES_ERROR_MESSAGE);
	}

	// set editted task attributes
	tasksVector[index-1].setEndTime(newEndTime);
	_task = tasksVector[index-1];

	// update internal data
	dataFeedback = _data->executeEdit(_task);

	if( dataFeedback == Data::FAILURE ) {
		logErrors(EDIT_ERROR_MESSAGE, _undoState);
		throw(EDIT_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	_data->getByID(tasks, taskID);
	rePopulateUIVector(tasks, tasksVector);
	tasks.clear();
	_data->getAll(tasks);
	_writer->write(tasks);

	statusMessage = EDIT_SUCCESS_MESSAGE;

	return statusMessage;
}

string Logic::editTaskByStartDateTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	int index;
	int taskID;
	ptime newStartDateTime;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool outOfBounds;

	// get user input
	index = parsedCommand.editTaskID;
	newStartDateTime = parsedCommand.editStartDateTime;

	outOfBounds = isOutOfBounds(index, tasksVector.size());

	if( outOfBounds ) {
		throw(INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE);
	}

	// find task on display
	taskID = tasksVector[index-1].getTaskID();

	if( tasksVector[index-1].getStartDateTime() == newStartDateTime ) {
		throw(NO_CHANGES_ERROR_MESSAGE);
	}

	// set editted task attributes
	tasksVector[index-1].setStartDateTime(newStartDateTime);
	_task = tasksVector[index-1];

	// update internal data
	dataFeedback = _data->executeEdit(_task);

	if( dataFeedback == Data::FAILURE ) {
		logErrors(EDIT_ERROR_MESSAGE, _undoState);
		throw(EDIT_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	_data->getByID(tasks, taskID);
	rePopulateUIVector(tasks, tasksVector);
	tasks.clear();
	_data->getAll(tasks);
	_writer->write(tasks);

	statusMessage = EDIT_SUCCESS_MESSAGE;

	return statusMessage;
}

string Logic::editTaskByEndDateTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	int index;
	int taskID;
	ptime newEndDateTime;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool outOfBounds;

	// get user input
	index = parsedCommand.editTaskID;
	newEndDateTime = parsedCommand.editEndDateTime;

	outOfBounds = isOutOfBounds(index, tasksVector.size());

	if( outOfBounds ) {
		throw(INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE);
	}

	// find task on display
	taskID = tasksVector[index-1].getTaskID();

	if( tasksVector[index-1].getEndDateTime() == newEndDateTime ) {
		throw(NO_CHANGES_ERROR_MESSAGE);
	}

	// set editted task attributes
	tasksVector[index-1].setEndDateTime(newEndDateTime);
	_task = tasksVector[index-1];

	// update internal data
	dataFeedback = _data->executeEdit(_task);

	if( dataFeedback == Data::FAILURE ) {
		logErrors(EDIT_ERROR_MESSAGE, _undoState);
		throw(EDIT_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);
	_data->getByID(tasks, taskID);
	rePopulateUIVector(tasks, tasksVector);
	tasks.clear();
	_data->getAll(tasks);
	_writer->write(tasks);

	statusMessage = EDIT_SUCCESS_MESSAGE;

	return statusMessage;
}
// **************************//
// END OF EDIT SUB-FUNCTIONS //
// **************************//


// ****************************//
// START OF DONE SUB-FUNCTIONS //
// ****************************//
// DONE marks a task as completed
string Logic::doneByIndex(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	int index;
	int taskID;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool outOfBounds;
	bool isNotMarked;
	bool markState;

	index = parsedCommand.doneTaskID;
	
	outOfBounds = isOutOfBounds(index, tasksVector.size());

	if( outOfBounds ) {
		throw(INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE);
	}

	taskID = tasksVector[index-1].getTaskID();

	// check done state of a task
	// if done mark undone
	// if undone mark done
	if( tasksVector[index-1].getDone() == true ) {
		isNotMarked = false;
		markState = false;
	} else {
		isNotMarked = true;
		markState = true;
	}

	// set task attributes
	_task = tasksVector[index-1];
	_task.setDone(markState);
	
	dataFeedback = _data->executeEdit(_task);

	if( dataFeedback == Data::FAILURE ) {
		logErrors(DONE_ERROR_MESSAGE, _undoState);
		throw(DONE_ERROR_MESSAGE);
	}

	if( dataFeedback == Data::SUCCESS ) {
		emptyTasksVector(tasksVector);
		_data->getByID(tasks, taskID);
		rePopulateUIVector(tasks, tasksVector);
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);
	}

	if( isNotMarked == true ) {
		statusMessage = DONE_SUCCESS_MESSAGE;
	} else if( isNotMarked == false ) {
		statusMessage = UNDONE_SUCCESS_MESSAGE;
	} else {
		statusMessage = DONE_SUCCESS_MESSAGE;
	}

	return statusMessage;
}

string Logic::doneBySearchTerm(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {
	
	string statusMessage;
	int taskID;
	bool multipleMatches;
	string searchTerm;
	Data::DataFeedback dataFeedback;
	vector<Task> tasks;
	bool isNotMarked;
	bool markState;

	searchTerm = parsedCommand.doneSearchTerm;

	dataFeedback = _data->getBySearchTerm(tasks, searchTerm);

	if( dataFeedback == Data::NOT_FOUND ) {
		throw(NOT_FOUND_ERROR_MESSAGE);
	}
	
	// found 1 match
	if( tasks.size() == FOUND_ONE_MATCH ) {
		_task = tasks[FIRST_POSITION];

		if( _task.getDone() == true ) {
			isNotMarked = false;
			markState = isNotMarked;
		} else {
			isNotMarked = true;
			markState = isNotMarked;
		}

		_task.setDone(markState);
		taskID = _task.getTaskID();
		dataFeedback = _data->executeEdit(_task);
		multipleMatches = false;
	}

	if( dataFeedback == Data::FAILURE ) {
		logErrors(DONE_ERROR_MESSAGE, _undoState);
		throw(DONE_ERROR_MESSAGE);
	}

	emptyTasksVector(tasksVector);

	if( multipleMatches ) {
		rePopulateUIVector(tasks, tasksVector);
		statusMessage = DONE_MULTIPLE_MATCH_ERROR_MESSAGE;
	} else {
		tasks.clear();
		_data->getByID(tasks, taskID);
		rePopulateUIVector(tasks, tasksVector);
		tasks.clear();
		_data->getAll(tasks);
		_writer->write(tasks);

		if( isNotMarked ) {
			statusMessage = DONE_SUCCESS_MESSAGE;
		} else  {
			statusMessage = UNDONE_SUCCESS_MESSAGE;
		}
	}

	return statusMessage;
}
// **************************//
// END OF DONE SUB-FUNCTIONS //
// **************************//


// ***********************************//
// START OF SORTING SUB-SUB-FUNCTIONS //
// ***********************************//
// sorting comparison functions for STL sort
bool Logic::sortAlpha(Task t1, Task t2) {
	return t1.getTaskName() < t2.getTaskName();
}

bool Logic::sortDateTime(Task t1, Task t2) {
	return t1.getStartTime() < t2.getStartTime();
}
// *********************************//
// END OF SORTING SUB-SUB-FUNCTIONS //
// *********************************//


// ****************************//
// START OF UNDO SUB-FUNCTIONS //
// ****************************//
// update state functions
void Logic::updateState() {
	// propagate the writers down the line
	// e.g.
	// state8 copied to state9
	// state7 copied to state8
	// state6 copied to state7
	// etc...
	for( int i=8; i>=0; i-- ) {
		states[i] = states[i+1];
	}

	// state0 is a copy of _writer
	// most recent state is state0
	states[0] = _writer;
}

// undo function
string Logic::undo(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector) {

	string statusMessage;
	bool exceedMaxUndo;

	exceedMaxUndo = updateUndoState();

	if( exceedMaxUndo ) {
		statusMessage = UNDO_ERROR_MESSAGE;
	} else {
		int undoState = getUndoState();
		updateMainWriter(undoState);
		statusMessage = UNDO_SUCCESS_MESSAGE;
	}

	return statusMessage;
}

// undo sub-functions
bool Logic::updateUndoState() {
	
	bool exceedMaxUndo;

	// increments _undoState
	// keeps track of current undo state
	_undoState++;
	int limit = getUndoState();

	if( limit > MAX_UNDO_STATES-1 ) {
		limit = MAX_UNDO_STATES-1;
		exceedMaxUndo = true;
	} else {
		exceedMaxUndo = false;
	}

	return exceedMaxUndo;
}

int Logic::getUndoState() {
	return _undoState;
}

void Logic::updateMainWriter(int undoState) {
	// update _writer (main)
	_writer = states[undoState];
}
// **************************//
// END OF UNDO SUB-FUNCTIONS //
// **************************//


// logging error messages
void Logic::logErrors(string errorMessage, int undoState) {
	fstream log;
	log.open(ERROR_LOG_FILE_NAME, fstream::in | fstream::app);

	log << errorMessage << endl;

	log.close();
}

// setup for when user first fires up program
void Logic::setupParser() {
	_parser = new Parser();
}

void Logic::setupData() {
	_data = new Data();
}

void Logic::setupMainWriter() {
	_writer = new Writer(FILE_NAME);
}

void Logic::setInitUndoState(int initUndoState) {
	_undoState = initUndoState;
}

void Logic::setupWriters() {

	stringstream fileName;

	for( int i=0; i<MAX_UNDO_STATES; i++ ) {
		fileName.str("");
		fileName << SUB_WRITER_FILE_NAME << i << FILE_NAME_EXTENSION;
		states[i] = new Writer(fileName.str());
	}
}

// read and store in Data
void Logic::populateData() {
	
	vector<Task> initVector;

	_writer->read(initVector);
	_data->executeLoad(initVector);
}

// constants
// status messages
const string Logic::INVALID_COMMAND_MESSAGE = "Invalid Command";

// success messages
const string Logic::ADD_SUCCESS_MESSAGE = "Task Added";
const string Logic::DELETE_SUCCESS_MESSAGE = "Task Deleted";
const string Logic::CLEAR_SUCCESS_MESSAGE = "File Contents Cleared";
const string Logic::LIST_SUCCESS_MESSAGE = "List Completed";
const string Logic::SORT_SUCCESS_MESSAGE = "Sort Completed";
const string Logic::SORT_INTERNAL_SUCCESS_MESSAGE = "File Contents Sorted";
const string Logic::SORT_ALPHA_SUCCESS_MESSAGE = "Sort By Alphabetical Completed";
const string Logic::SORT_FLAG_SUCCESS_MESSAGE = "Sort By Flag Completed";
const string Logic::SORT_DATE_TIME_SUCCESS_MESSAGE = "Sort By Date Time Completed";
const string Logic::EDIT_SUCCESS_MESSAGE = "Edit Completed";
const string Logic::DONE_SUCCESS_MESSAGE = "Marked Done";
const string Logic::UNDONE_SUCCESS_MESSAGE = "Marked Un-Done";
const string Logic::SEARCH_SUCCESS_MESSAGE = "Search Completed";
const string Logic::FLAG_SUCCESS_MESSAGE = "Item Flagged";
const string Logic::UNFLAG_SUCCESS_MESSAGE = "Item Un-flagged";
const string Logic::UNDO_SUCCESS_MESSAGE = "Undo Successful";

// error messages
const string Logic::ADD_ERROR_MESSAGE = "Unable To Add";
const string Logic::ADD_EXIST_ERROR_MESSAGE = "Task Already Exist";
const string Logic::ADD_ERROR_CLASH_MESSAGE = "Clash";
const string Logic::DELETE_ERROR_MESSAGE = "Unable To Delete";
const string Logic::CLEAR_ERROR_MESSAGE = "File Is Empty";
const string Logic::LIST_ERROR_MESSAGE = "Unable To List";
const string Logic::SORT_ERROR_MESSAGE = "Unable to Sort";
const string Logic::SORT_FLAG_ERROR_MESSAGE = "Unable to Sort Flagged";
const string Logic::SORT_DATE_TIME_ERROR_MESSAGE = "Unable to Sort Date Time";
const string Logic::SORT_DONE_ERROR_MESSAGE = "Unable to Sort Done";
const string Logic::EDIT_ERROR_MESSAGE = "Unable to Edit";
const string Logic::DONE_ERROR_MESSAGE = "Unable to Mark Done";
const string Logic::DONE_MULTIPLE_MATCH_ERROR_MESSAGE = "Multiple Matches Found";
const string Logic::FLAG_ERROR_MESSAGE = "Unable To Flag";
const string Logic::UNFLAG_ERROR_MESSAGE = "Unable to Un-flag";
const string Logic::UNDO_ERROR_MESSAGE = "Unable to Undo Further";
const string Logic::NO_CHANGES_ERROR_MESSAGE = "No Changes Made";
const string Logic::NOT_FOUND_ERROR_MESSAGE = "Task Not Found";
const string Logic::INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE = "Invalid Task Number";

// number of undo states
const int Logic::INIT_UNDO_STATE = 0;
const int Logic::MAX_UNDO_STATES = 10;

// keywords
const string Logic::DELETE_TYPE_DONE = "done";

// main file name
const string Logic::FILE_NAME = "schedulerPP.txt";
const string Logic::ERROR_LOG_FILE_EOF = "========END========";

// sub-writers file name
const string Logic::SUB_WRITER_FILE_NAME = "subwriter";
const string Logic::FILE_NAME_EXTENSION = ".txt";

// error log file name
const string Logic::ERROR_LOG_FILE_NAME = "log.txt";

// positions
const int Logic::FOUND_ONE_MATCH = 1;
const int Logic::FIRST_POSITION = 0;

// task related
const string Logic::APPEND_DUPLICATED_OPEN = "(";
const string Logic::APPEND_DUPLICATED_CLOSE = ")";

// *********************************** //
// START OF CONSTRUCTOR AND DESTRUCTOR //
// *********************************** //
Logic::Logic() {

	// sets up the program before user begins using it
	// populates the internal vector with tasks from files
	setupParser();
	setupData();
	setupMainWriter();

	// 10 different undo states
	// it will be cycled when a command is successfully invoked
	setupWriters();
	setInitUndoState(INIT_UNDO_STATE);
	populateData();
}

Logic::~Logic() {
	fstream log;
	log.open(ERROR_LOG_FILE_NAME, fstream::in | fstream::app);

	log << ERROR_LOG_FILE_EOF << endl;

	log.close();

	delete _parser;
	delete _data;
	delete _writer;

	for( int i=0; i<MAX_UNDO_STATES; i++ ) {
		delete states[i];
	}
}
// ********************************* //
// END OF CONSTRUCTOR AND DESTRUCTOR //
// ********************************* //




// helper functions
void Logic::emptyTasksVector(vector<Task>& tasksVector) {
	while( !tasksVector.empty() ) {
		tasksVector.pop_back();
	}
}

// this function re-populates the UI display vector
void Logic::rePopulateUIVector(vector<Task>& tasks, vector<Task>& tasksVector) {
	for( unsigned int i=0; i<tasks.size(); i++ ) {
		tasksVector.push_back(tasks[i]);
	}
}

bool Logic::isOutOfBounds(int index, int size) {
	bool outOfBounds;

	if( index <= 0 ) {
		outOfBounds = true;
	} else if( index > size ) {
		outOfBounds = true;
	} else {
		outOfBounds = false;
	}

	return outOfBounds;
}


// redundant functions to be removed
void Logic::copyVectorContents(vector<Task>& fromVect, vector<Task>& toVect) {
	for( unsigned int i=0; i<fromVect.size(); i++ ) {
		toVect.push_back(fromVect[i]);
	}
}